#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

#define WORKER_COUNT 5
#define GENERATOR_COUNT 5
#define STATUS_READY 0
#define STATUS_PROCESSING 1
#define STATUS_FINISH 2

struct work{
	int worker;
	int num;
	int id;
	pthread_rwlock_t rwlock;
	int status;
	time_t sttime;
	time_t endtime;
	struct work* next;
};
int count=0;

struct work* work_queue=NULL;
pthread_rwlock_t queue_rwlock;
struct work* finish_queue=NULL;
pthread_rwlock_t finish_rwlock;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock=PTHREAD_MUTEX_INITIALIZER;

struct work* add_work(struct work*,pthread_rwlock_t*,struct work*);

void* monitor_status(void * arg){
	struct work* work[2];
	struct work* head;
	int i=0;

	while(1){
		pthread_rwlock_rdlock(&queue_rwlock);
		pthread_rwlock_rdlock(&finish_rwlock);
		work[0]=work_queue;
		work[1]=finish_queue;
		for(i=0;i<2;i++){
			head=work[i];
			if(i==0)
				printf("\n\n work queue:\n");
			if(i==1)
				printf("\n\n finish queue:\n");
			//head=work_queue;
			if(head==NULL){
				printf("monitor:no work item\n");
			}

			struct work* tmp=head;
			while(tmp!=NULL){
				printf("monitor: id=%d, worker=%d,num=%d,status=%d,sttime=%d,endtime=%d\n",tmp->id,(int)(tmp->worker),tmp->num,tmp->status,tmp->sttime,tmp->endtime);
				tmp=tmp->next;
			}
		}
		pthread_rwlock_unlock(&finish_rwlock);
		pthread_rwlock_unlock(&queue_rwlock);
		usleep(9000);
	}
}

void * work_generator(void* arg){
	while(1){
		struct work* wi=malloc(sizeof(struct work));
		wi->id=count++;
		wi->worker=0;
		wi->num=1;
		wi->status=STATUS_READY;
		wi->next=NULL;
		wi->sttime=time(&wi->sttime);
		wi->endtime=0;

		work_queue=add_work(wi,&queue_rwlock,work_queue);
		pthread_cond_signal(&cond);
		//printf("worker:add work\n");
		//usleep(random()%1000+100);
		sleep(1);
	}
	
}

struct work* get_work(int id){
/*	pthread_mutex_lock(&qlock);
	pthread_cond_wait(&cond,&qlock);
	pthread_mutex_unlock(&qlock);
	*/

	pthread_rwlock_rdlock(&queue_rwlock);
	if(work_queue==NULL){
		pthread_rwlock_unlock(&queue_rwlock);
		return NULL;
	}
	struct work* tmp=work_queue;
	while(tmp->worker!=0 && tmp->next!=NULL){
		tmp=tmp->next;
	}
	if(tmp->worker==0){
		tmp->worker=id;
		pthread_rwlock_unlock(&queue_rwlock);
		return tmp;
	}

	pthread_rwlock_unlock(&queue_rwlock);
	return NULL;
}

void remove_work(struct work* wi){
//	printf("remove work %d\n",wi->worker);
	pthread_rwlock_wrlock(&queue_rwlock);
	struct work* tmp=work_queue;
	if(wi==tmp){
		work_queue=wi->next;
	}else{
		while(tmp->next!=wi){
			tmp=tmp->next;
		}
		tmp->next=wi->next;
	}
	//free(wi);
	pthread_rwlock_unlock(&queue_rwlock);
	wi->next=NULL;
	finish_queue=add_work(wi,&finish_rwlock,finish_queue);
}

void* worker(void* args){
	struct work* wi;
	while(1){
		usleep(50);
		wi=get_work((int)pthread_self());
		if(wi==NULL){
			//TODO:implement wait
			continue;
		}
		//printf("worker %d process work\n",pthread_self());
		while(wi->num<100){
			wi->num++;
			usleep(5);
		}
		sleep(2);
		wi->status=STATUS_FINISH;
		wi->endtime=time(&wi->endtime);
		remove_work(wi);
	}
}

struct work* add_work(struct work* w,pthread_rwlock_t* rwlock,struct work* head){
	//printf("add work:%x, %x ,%x\n",head,w,work_queue);
	pthread_rwlock_wrlock(rwlock);
	if(head==NULL){
		head=w;
		pthread_rwlock_unlock(rwlock);
		return w;
	}
	struct work* tmp=head;
	while(tmp->next!=NULL){
		tmp=tmp->next;
	}
	tmp->next=w;
	pthread_rwlock_unlock(rwlock);
	return head;
}

int main(){
	pthread_t generators[GENERATOR_COUNT];
	pthread_t workers[WORKER_COUNT];

	time_t t;
	srandom(time(&t));
	pthread_t tid;
	pthread_rwlock_init(&queue_rwlock,NULL);

	int i;
	for(i=0;i<GENERATOR_COUNT;i++){
		pthread_create(&generators[i],NULL,work_generator,NULL);
	}	
	for(i=0;i<WORKER_COUNT;i++){
		pthread_create(&workers[i],NULL,worker,NULL);
	}	
	pthread_t monitor;

	pthread_create(&monitor,NULL,monitor_status,NULL);
	
	pthread_join(monitor,NULL);
}


